package configure

import (
	"crypto/md5"
	"errors"
	"fmt"
	"gitee.com/Luna-CY/hui-hui/internal/logger"
	"gitee.com/Luna-CY/hui-hui/internal/runtime"
	"gitee.com/Luna-CY/hui-hui/internal/util/interactive"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/mdp/qrterminal/v3"
	"github.com/pelletier/go-toml/v2"
	"github.com/pquerna/otp/totp"
	"os"
	"strings"
)

func GenerateWithInitializer(hostname string) error {
	var configure = new(c)
	configure.Server.Hostname = hostname
	configure.Server.GinMode = gin.ReleaseMode
	configure.Server.Port = defaultPort

	var password = strings.Split(uuid.New().String(), "-")[0]
	logger.GetLogger().Sugar().Infof("HuiHui的默认登录密码为: %s", password)

	configure.Server.Authentication.Type = AuthenticationTypeFixed
	configure.Server.Authentication.Password = fmt.Sprintf("%x", md5.Sum([]byte(password)))

	config, err := os.OpenFile(runtime.MainConfig, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	if nil != err {
		return err
	}

	defer func() {
		_ = config.Close()
	}()

	if err := toml.NewEncoder(config).Encode(configure); nil != err {
		return err
	}

	return nil
}

// GenerateWithInteractive 交互式的生成配置
func GenerateWithInteractive(hostname string) error {
	var configure = new(c)
	configure.Server.GinMode = gin.ReleaseMode

	// 端口
	port, err := interactive.ReceiveIntegerWithDefault("请选择Hui-Hui监听的本地端口", 1, 65535, defaultPort)
	if nil != err {
		return err
	}

	configure.Server.Port = port

	// 身份验证
	value, err := interactive.SelectOne("请选择管理面板的身份验证类型", []string{string(AuthenticationTypeFixed), string(AuthenticationTypeEmailCode), string(AuthenticationTypeTOTP)})
	if nil != err {
		return err
	}

	switch AuthenticationType(value) {
	case AuthenticationTypeFixed:
		configure.Server.Authentication.Type = AuthenticationTypeFixed

		password, err := interactive.ReceiveWithLength("请输入密码，最短6位，最长32位，允许包含任意字符，包括中文", 6, 32, false)
		if nil != err {
			return err
		}

		repassword, err := interactive.ReceiveWithLength("请再次输入密码", 6, 32, false)
		if nil != err {
			return err
		}

		if password != repassword {
			return errors.New("两次密码不一致")
		}

		configure.Server.Authentication.Password = fmt.Sprintf("%x", md5.Sum([]byte(password)))
	case AuthenticationTypeEmailCode:
		configure.Server.Authentication.Type = AuthenticationTypeEmailCode

		email, err := interactive.Receive("请输入接收验证码的邮件地址", false)
		if nil != err {
			return err
		}

		reinput, err := interactive.Receive("请再次输入接收验证码的邮件地址", false)
		if nil != err {
			return err
		}

		if email != reinput {
			return errors.New("两次输入的邮件地址不一致")
		}

		configure.Server.Authentication.Email = email

		// 接收SMTP服务器配置
		host, err := interactive.Receive("请输入SMTP服务器的域名，输入[-]将跳过SMTP服务器的配置", false)
		if nil != err {
			return err
		}

		if "-" != host {
			configure.Smtp.Host = host

			port, err := interactive.ReceiveInteger("请输入SMTP服务器的端口号，范围[1-65535]", 1, 65535, false)
			if nil != err {
				return err
			}

			configure.Smtp.Port = port

			username, err := interactive.Receive("请输入用于登录SMTP服务器的用户名，可以为空", true)
			if nil != err {
				return err
			}

			configure.Smtp.Username = username

			if "" != username {
				password, err := interactive.Receive("请输入用于登录SMTP服务器的密码，可以为空", true)
				if nil != err {
					return err
				}

				configure.Smtp.Password = password
			}

			from, err := interactive.Receive("请输入发信人邮件地址", false)
			if nil != err {
				return err
			}

			configure.Smtp.From = from
		}
	case AuthenticationTypeTOTP:
		configure.Server.Authentication.Type = AuthenticationTypeTOTP

		account, err := interactive.Receive("请输入一个名字用于标记此生成器", false)
		if nil != err {
			return err
		}

		key, err := totp.Generate(totp.GenerateOpts{Issuer: hostname, AccountName: account})
		if nil != err {
			return err
		}

		configure.Server.Authentication.Secret = key.Secret()
		logger.GetLogger().Sugar().Info("您选择了TOTP验证模式，用于创建验证器的参数与二维码将将显示在下方，请使用验证器扫描，注意不要将参数串与二维码交给其他人，注意没有备份秘钥，若验证器遗失可通过命令`hui-hui config regenerate totp`重新生成")
		logger.GetLogger().Sugar().Info(key.String())
		qrterminal.Generate(key.String(), qrterminal.M, os.Stdout)
	}

	config, err := os.OpenFile(runtime.MainConfig, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	if nil != err {
		return err
	}

	defer func() {
		_ = config.Close()
	}()

	if err := toml.NewEncoder(config).Encode(configure); nil != err {
		return err
	}

	return nil
}
